#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "dnsmasq.h" //config
#ifdef HAVE_GUARD

//url in list are entered in a tree, if a path exist, the url is in the list

struct node {
  struct node* sons[39];
  int all;
};

// > RFC 1034 section 3.5: The labels must follow the rules for ARPANET host
// > names.  They must start with a letter, end with a letter or digit, and
// > have as interior characters only letters, digits, and hyphen.  There are
// > also some restrictions on the length.  Labels must be 63 characters or
// > less.
// hash function for node indexation
int reduce(char c) {
  if (c == '0') return 0;
  if ('1' <= c && c <= '9') return c - '1' + 1; // 1=>9
  if (c == '-') return 10;
  if (c == '.') return 11;
  if (c == '_') return 12; //this char is currently accepted IRL
  if ('a' <= c && c <= 'z') return c - 'a' + 13;
  if ('A' <= c && c <= 'Z') return c - 'A' + 13;
  
  return -1;
}
// ajoute un pattern dans l arbre des url possible
int add_rev_tree(struct node* n,char* haystack) {
  struct node * p = n;
  int cpt = strlen(haystack);
  if ( cpt > 1 && haystack[cpt-1] == 10) cpt--;
  for ( --cpt; cpt >= 0; --cpt) {
    if ( haystack[cpt] == '*' ) {
      p->all = 1;
      break;
    }
    int o = reduce(haystack[cpt]);
    if ( o == -1) {
      fprintf( stderr, "ignore %c: %s\n", haystack[cpt], haystack );
      return 0;
    }
    //syslog
    if ( p->sons[o] == NULL ) {
      p->sons[o] = calloc(1,sizeof(struct node));
      if ( p->sons[o] == NULL) {
        fprintf( stderr, "out of memmory\n");
        return 255;
      }
    }
    p = p->sons[o];
  }
  return 0;
}

//repond 1 si l url needle est dans l arbre n
int in(struct node* n, char* needle) {
  struct node * p = n;
  fprintf(stderr, "in(%s)\n", needle);
  int cpt = strlen(needle);
  for ( --cpt; cpt >= 0; --cpt) {
    int o = reduce(needle[cpt]);
    if ( o == -1) {
      fprintf( stderr, "ignore %c: %s\n", needle[cpt], needle );
      return 0;
    }
    p = p->sons[o];
    if ( p == NULL ) return 0;
    if ( p->all ) return 1;
  }
  return 1;
}

struct node white = {{},-1};
struct node black = {{},-1};

int blacklist(char *d) {
  if ( black.all == -1 ) return 0;
  if ( white.all != -1 ) return 0;
  return in(&black,d);
  
}

int whitelist(char *d){
  if ( white.all == -1 ) return 1;
  return in(&white,d);
}

void load_guard(char* ip) {
  char b[96];
#ifndef FILTER_TEST
  memcpy ( daemon->dnsreplyip, "\xc0\x0c\0\x01\0\x01\0\0\0\0\0\x04", 17);
  inet_pton(AF_INET, ip, &(daemon->dnsreplyip[12]));
#endif
  //fprintf( stderr, "reply: %s\n", ip );
  FILE * f = fopen(WHITEFILE, "r");
  if ( f ) {
    white.all = 0;
    while ( fgets(b, 96, f) ) {
      add_rev_tree( &white, b);
      //fprintf( stderr, "white: %s", b );
    }
    fclose(f);
    return;
  }
  f = fopen(BLACKFILE, "r");
  if ( f ) {
    black.all = 0;
    while ( fgets(b, 96, f) ) {
      add_rev_tree( &black, b);
      //fprintf( stderr, "black: %s", b );
    }
    fclose(f);
  }
}
#endif
#ifdef FILTER_TEST
struct node rev_data = {};
char* rev_pattern[] = {
  "*.renault.fr",
  "*.bentley.co.uk",
  "*.cpasduporno.fr",
  "*.cpasduporno1324567890.fr",
  "*.noads.co.uk",
  "www.google.fr",
  NULL
};

int main(int i, char** c) {
  fprintf(stderr, "char : %d\n", sizeof(char));
  char** white_list = rev_pattern;
  while (*white_list) {
    add_rev_tree( &rev_data, *white_list);
    white_list++;
  }
  
  char *set = ".1234567890abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ_+";
  for (;*set != '\0';set++) {
    fprintf( stderr, "reduce %c => %d\n", *set, reduce(*set));
  }

  fprintf( stderr, "reply %d\n", in(&rev_data,"lol.renault.fr") );
  fprintf( stderr, "reply %d\n", in(&rev_data,"lol.facebook.fr") );
  fprintf( stderr, "reply %d\n", in(&rev_data,"facebook.com") );
  fprintf( stderr, "reply %d\n", in(&rev_data,"facEBook.com") );
  fprintf( stderr, "reply %d\n", in(&rev_data,"toto.lol") );
  fprintf( stderr, "reply %d\n", in(&rev_data,"a.b.c.d.e.f.renault.fr") );
  fprintf( stderr, "reply %d\n", in(&rev_data,"a.b.c.d.e.f.cpasduporno.fr") );
  fprintf( stderr, "reply %d\n", in(&rev_data,"a.cpasduporno1324567890.fr") );
  fprintf( stderr, "reply %d\n", in(&rev_data,"www.google.fr") );
  fprintf( stderr, "reply %d\n", in(&rev_data,"www.google.com") );
  return 0;
}
#endif
